<script setup lang="tsx">
/**
 * @name RoleManagement
 * @description 角色管理
 */
import { Box, Delete, Edit } from '@element-plus/icons-vue'
import type {
  Action,
  FormInstance,
} from 'element-plus'
import {
  ElButton,
  ElDivider,
  ElTooltip,
  ElTree,
} from 'element-plus'
import { uniqBy } from 'lodash-es'
import FormModal from './components/FormModal.vue'
import SearchForm from './components/SearchForm.vue'
import {
  defaultPagingParams,
  defaultRoleForm,
  rules,
} from './index'
import type {
  ApplicationInfo,
  ApplicationMenuInfo,
  ApplicationQueryBindMenuIdByManyResult,
  DictionaryEncoInfo,
  RoleInfo,
  RoleQueryListParams,
  RoleSaveOrUpdateParams,
  TableColumn,
} from '@/types'
import {
  sysApplicationApi,
  sysApplicationMenuApi,
  sysDictionaryEncoApi,
  sysRoleApi,
  sysRoleApplicationApi,
} from '@/api'

// ------------------use------------------
// #region
const { getPower } = usePagePower()
const { withLoading, loading, vLoading }
  = useDefineLoading()
const [tableData, setTableData] = useState<RoleInfo[]>([])
const [roleForm, setRoleForm]
  = useState<RoleSaveOrUpdateParams>({
    ...defaultRoleForm,
  })
const [total, setTotal] = useState<number>(0)
const [formModalTitle, setFormModalTitle]
  = useState<string>('')
const { $message, $confirm } = useCurrentInstance()
const [isAssignmentsMenu, setIsAssignmentsMenu]
  = useState<boolean>(false)
// 选择角色
const [selectRoleId, setSelectRoleId] = useState<number>(0)
/** 分配菜单选中菜单树节点数据 */
const [selectTreeNodeData, setSelectTreeNodeData]
  = useState(
    {} as Omit<ApplicationQueryBindMenuIdByManyResult, 'id'>,
  )
// 应用列表
const [applyList, setApplyList] = useState<
  ApplicationInfo[]
>([])
// const [selectApplyId, setSelectApplyTab] = useState<string>('');
// 获取应用绑定菜单
const [bindMenuTree, setBindMenuTree] = useState<
  ApplicationMenuInfo[]
>([])
// 弹窗应用资源选项
const [bindApplyId, setBindApplyId] = useState<number[]>(
  [],
)
const [powerData, setPowerData] = useState<
  DictionaryEncoInfo[]
>([])
const [bindMenuInfo, setBindMenuInfo] = useState<
  Omit<ApplicationQueryBindMenuIdByManyResult, 'id'>[]
>([])
// #endregion
// ------------------静态变量------------------
// #region
const columns: TableColumn<RoleInfo>[] = [
  {
    key: uuid(),
    type: 'index',
    label: '#',
  },
  {
    key: uuid(),
    prop: 'roleName',
    label: '角色名称',
  },
  {
    key: uuid(),
    fixed: 'right',
    label: '操作',
    width: '150',
    render({ row }) {
      return (
        <>
          <ElTooltip content="编辑" placement="top">
            <ElButton
              link
              disabled={!getPower('edit')}
              type="primary"
              icon={Edit}
              onClick={() => showEditDialog(row)}
            >
            </ElButton>
          </ElTooltip>
          <ElDivider direction="vertical" />
          <ElTooltip content="分配菜单" placement="top">
            <ElButton
              link
              disabled={!getPower('edit')}
              type="warning"
              icon={Box}
              onClick={() =>
                showAssignmentsMenuDialog(row.id)}
            >
            </ElButton>
          </ElTooltip>
          <ElDivider direction="vertical" />
          <ElTooltip content="删除" placement="top">
            <ElButton
              link
              disabled={!getPower('delete')}
              type="danger"
              icon={Delete}
              onClick={() => deleteData(row.id)}
            >
            </ElButton>
          </ElTooltip>
        </>
      )
    },
  },
]
// #endregion
// ------------------动态变量------------------
// #region

// #endregion
// ------------------ref-----------
// #region
const pagingParams = ref<RoleQueryListParams>({
  ...defaultPagingParams,
})
const selectApplyId = ref<number>()
// const bindApplyId = ref<number[]>([]);
const modalRef = ref<InstanceType<typeof FormModal>>()
const treeRef = ref<InstanceType<typeof ElTree>>()
const formRef = ref<FormInstance | null>(null)
// #endregion
// ------------------reactive------
// #region

// #endregion
// ------------------computed------
// #region
const computedSelectTreeNodePower = computed({
  get() {
    if (
      selectTreeNodeData.value.power === '0'
      || !selectTreeNodeData.value.power
    ) {
      return powerData.value.map(item => item.dicKey)
    }
    else {
      return selectTreeNodeData.value.power.split(',')
    }
  },
  set(v: string[]) {
    const row = bindMenuInfo.value.find(
      item =>
        item.menuId === selectTreeNodeData.value.menuId,
    )
    if (!row)
      return
    const m = v.join(',')
    row.power = m
    selectTreeNodeData.value.power = m
  },
})
// #endregion
// ------------------生命周期------------------
// #region
onMounted(() => {
  getTableData()
  getApplyList()
  getPowerData()
})
// #endregion
// ------------------内置方法---------------------
// #region

// #endregion
// ------------------方法---------------------
// #region
function handleTreeCheckChange(node: ApplicationMenuInfo, bol: boolean) {
  let row = bindMenuInfo.value.find(
    item => item.menuId === node.menuId,
  )
  if (!row) {
    row = {
      menuId: node.menuId,
      roleId: selectRoleId.value,
      power: '0',
    }
  }
  const m = bindMenuInfo.value.findIndex(
    item => item.menuId === node.menuId,
  )
  if (m === -1) {
    bol && bindMenuInfo.value.push({ ...row })
  }
  else {
    !bol && bindMenuInfo.value.splice(m, 1)
  }
  // const currentKey: number = treeRef.value?.getCurrentKey();
  if (bol) {
    setSelectTreeNodeData({ ...row })
    treeRef.value?.setCurrentKey(node.menuId)
    // console.log(treeRef.value?.getCurrentKey());
  }
  else {
    setSelectTreeNodeData({} as any)
    treeRef.value?.setCurrentKey()
  }
}
function handleTreeNodeClick(node: ApplicationMenuInfo) {
  const row = bindMenuInfo.value.find(
    item => item.menuId === node.menuId,
  )
  // if (!row) {
  //   row = {
  //     menuId: node.menuId,
  //     roleId: selectRoleId.value,
  //     power: '0'
  //   };
  // }
  if (!row) {
    setSelectTreeNodeData({} as any)
    return
  }
  setSelectTreeNodeData({ ...row })
}
/** 获取应用绑定的菜单 */
async function getBindMenuTree() {
  if (applyList.value.length === 0)
    return

  await nextTick()
  if (!selectApplyId.value) {
    $message.error('请先选择左侧应用！')
    return
  }
  const { data: res } = await withLoading(
    sysApplicationMenuApi.queryBindMenuTree,
    'bindMenu',
  )({
    applicationId: selectApplyId.value,
  })
  setBindMenuTree(res)
  await nextTick()
  // 加载角色绑定的菜单id
  getBindMenuInfo()
}
async function getPowerData() {
  const { data: res }
    = await sysDictionaryEncoApi.queryList('QUAN_XIAN')
  setPowerData(res)
}
/** 获取绑定的菜单id */
async function getBindMenuInfo() {
  if (!selectApplyId.value)
    return
  const { data: res } = await withLoading(
    sysApplicationMenuApi.queryBindMenuIdByMany,
    'bindMenu',
  )({
    applicationId: selectApplyId.value,
    roleId: selectRoleId.value,
  })
  removeUnnecessaryParams(res, ['id'])

  setBindMenuInfo(uniqBy(res, 'menuId'))
  treeRef.value?.setCheckedKeys(
    res.map(item => item.menuId),
  )
}
/** 获取绑定的应用id */
async function getBindApplyId() {
  const { data: res }
    = await sysRoleApplicationApi.queryBindApplicationId(
      selectRoleId.value,
    )
  setBindApplyId(res)
}
/** 处理应用tabs 切换 */
function handleApplyChange() {
  // if (!selectApplyId.value) {
  //   setSelectApplyTab(applyList.value[0]?.applicationName!);
  // }
  getBindMenuTree()
  // 选中节点重置
  setSelectTreeNodeData({} as any)
}
async function getTableData() {
  const { data: res } = await withLoading(
    sysRoleApi.queryList,
  )(pagingParams.value)
  setTableData(res.records)
  setTotal(res.total)
}
/** 获取应用列表 */
async function getApplyList() {
  const { data: res } = await sysApplicationApi.queryList()
  setApplyList(res)
}

/** 删除数据 */
async function deleteData(id: number) {
  const res: Action = await $confirm(
    '该操作将永久删除该角色，确定删除？',
    '警告',
    {
      type: 'warning',
    },
  ).catch(e => e)

  if (res !== 'confirm')
    return
  const { code } = await sysRoleApi.deleteById(id)
  if (code !== 200)
    return
  getTableData()
}
async function saveOrUpdate(params: Partial<RoleSaveOrUpdateParams>) {
  removeUnnecessaryParams(params)
  const { code } = await sysRoleApi.saveOrUpdate(params)
  if (code !== 200)
    return
  getTableData()
  modalRef.value?.close()
  $message.success('保存成功！')
}
async function assignment() {
  const isValid = await formRef.value?.validate()
  if (!isValid)
    return

  // if (!keys || keys?.length === 0) {
  //   $message.warning('请先选择菜单！');
  //   return;
  // }
  const bol1 = await assignmentsMenu()
  if (bol1) {
    const bol = await assignmentsApply()
    setIsAssignmentsMenu(false)
    if (bol) {
      $message.success('菜单分配成功！')
    }
  }
}
async function assignmentsApply() {
  const n = bindApplyId.value.map(key => ({
    applicationId: key as number,
    roleId: selectRoleId.value,
  }))
  const { code } = await withLoading(
    sysRoleApplicationApi.batchApplicationId,
    'bindMenu',
  )(n)
  if (code !== 200)
    return false
  return true
}
async function assignmentsMenu() {
  if (!selectApplyId.value) {
    $message.warning('请先选择左侧应用！')
    return
  }
  const list = [...bindMenuInfo.value]
  const { code } = await withLoading(
    sysRoleMenuApi.batchMenuId,
    'bindMenu',
  )({
    list,
    applicationId: selectApplyId.value,
  })
  if (code !== 200)
    return false
  return true
}
async function showAddDialog() {
  modalRef.value?.open()
  await nextTick()
  setFormModalTitle('新增')
  setRoleForm({
    ...defaultRoleForm,
  })
}
async function showEditDialog(row: RoleSaveOrUpdateParams) {
  modalRef.value?.open()
  await nextTick()
  setFormModalTitle('编辑')
  setRoleForm({
    ...defaultRoleForm,
    ...row,
  })
}
async function showAssignmentsMenuDialog(id: number) {
  // 当前操作的角色id
  setSelectRoleId(id)
  // 加载绑定的应用资源id
  getBindApplyId()
  // 弹窗显示
  setIsAssignmentsMenu(true)
  // 菜单数据重置
  setBindMenuTree([])
  // 选中节点重置
  setSelectTreeNodeData({} as any)
  selectApplyId.value = undefined
  // 表单重置
  formRef.value?.resetFields()
}
// #endregion
</script>

<template>
  <div
    class="padding-sm flex-direction flex align-end container"
  >
    <view-table
      :loading="loading"
      :columns="columns"
      :data="tableData"
      :show-download="false"
    >
      <template #actions>
        <SearchForm
          v-model:query="pagingParams"
          @plus="showAddDialog"
          @reset="getTableData"
          @search="getTableData"
        />
      </template>
    </view-table>

    <!-- 分页 -->
    <ListsPagination
      v-model:page-num="pagingParams.pageNumber"
      v-model:page-size="pagingParams.pageSize"
      :total="total"
      @current-change="getTableData"
      @size-change="getTableData"
    />
    <FormModal
      ref="modalRef"
      :title="formModalTitle"
      :data="roleForm"
      @save-success="saveOrUpdate(roleForm)"
    />
    <!-- 分配菜单 -->
    <el-dialog
      id="bindMenu"
      v-model="isAssignmentsMenu"
      width="600"
      title="分配菜单"
      append-to-body
    >
      <el-form
        ref="formRef"
        :model="{ bindApplyId }"
        :rules="rules"
        label-width="60px"
        label-position="right"
        inline
        class="margin-top-sm"
      >
        <el-form-item
          label="应用"
          prop="bindApplyId"
          class="width-fill"
        >
          <el-select
            v-model="bindApplyId"
            multiple
            collapse-tags
            filterable
            placeholder="请选择应用"
            class="width-fill"
          >
            <el-option
              v-for="item in applyList"
              :key="item.id"
              :label="item.applicationName"
              :value="item.id"
            />
          </el-select>
        </el-form-item>
      </el-form>
      <div
        v-loading="{
          target: '#bindMenu',
          space: 'bindMenu',
        }"
        class="tree-container align-center width-fill box-border flex gap-xs"
        :class="[{ mask: bindApplyId.length === 0 }]"
        style="height: 40vh"
      >
        <el-tabs
          v-model="selectApplyId"
          tab-position="left"
          class="flex-incompressible height-fill"
          @tab-change="handleApplyChange"
        >
          <template
            v-for="item in applyList"
            :key="item.id"
          >
            <el-tab-pane
              v-if="bindApplyId.includes(item.id)"
              :label="item.applicationName"
              :name="item.id"
            />
          </template>
        </el-tabs>
        <b-scrollbar
          class="padding-0 height-fill flex-fillRemaining"
        >
          <ElTree
            ref="treeRef"
            check-strictly
            accordion
            highlight-current
            :props="{
              label: 'menuName',
            }"
            node-key="menuId"
            :data="bindMenuTree"
            show-checkbox
            @node-click="handleTreeNodeClick"
            @check-change="handleTreeCheckChange"
          />
        </b-scrollbar>
        <b-scrollbar
          class="flex-incompressible"
          style="width: 100px"
        >
          <el-checkbox-group
            v-model="computedSelectTreeNodePower"
            :disabled="
              Object.keys(selectTreeNodeData).length === 0
            "
            class="flex-direction flex gap-xs"
          >
            <el-checkbox
              v-for="item in powerData"
              :key="item.id"
              :value="item.dicKey"
              class="!margin-0"
              border
            >
              {{ item.dicValue }}
            </el-checkbox>
          </el-checkbox-group>
        </b-scrollbar>
      </div>

      <template #footer>
        <ElButton @click="setIsAssignmentsMenu(false)">
          取 消
        </ElButton>
        <ElButton type="primary" @click="assignment">
          确 定
        </ElButton>
      </template>
    </el-dialog>
  </div>
</template>

<style lang="scss" scoped>
//------------------组件样式------------------
.tree-container.mask {
  position: relative;
  &::before {
    display: grid;
    place-content: center;
    font-size: 20px;
    color: var(--el-color-primary);
    content: '请分配应用！';
    position: absolute;
    z-index: 9;
    top: 0px;
    left: 0px;
    width: inherit;
    height: inherit;
    border: 1px solid currentColor;
    border-radius: 6px;
    background-color: rgba(64, 158, 255, 0.2);
    backdrop-filter: blur(4px);
  }
}

//-------------样式穿透-------------
</style>
